<?php
declare(strict_types=1);

namespace app\helper;

use app\model\CrontabLog;
use app\model\Download;
use app\model\TorrentBank;
use app\api\QbApi;
use app\api\TrApi;
use Rhilip\Bencode\Bencode;
use Rhilip\Bencode\ParseException;
use Exception;

/**
 * 种子库计算助手
 */
class TorrentHelper
{
    private $processedCount = 0;
    private const BATCH_SIZE = 100; // 分批保存到数据库的条目数
    private $currentBatch = [];

    public function index(int $id = 0)
    {
        $start_time = microtime(true);

        // 获取需要处理的种子总数
        $totalSeeds = Download::where('status', '=', 1)->count();

        foreach (Download::where('status', '=', 1)->select() as $download) {
            CrontabLog::create(['crontab_id' => $id, 'exception' => '正在执行下载器' . $download['name'] . '种子库计算任务,请耐心等待']);

            $save_path = $download['save_path'];
            if (!is_dir($save_path)) {
                CrontabLog::create(['crontab_id' => $id, 'exception' => "非文件夹或不存在, 请检查[种子目录]映射是否正确"]);
                continue;
            }

            $download_bank = TorrentBank::where("download_id", $download['id'])->select();
            if (count($download_bank) > 0) {
                foreach ($download_bank as $key => $value) {
                    $file_name = $save_path . DIRECTORY_SEPARATOR . $value['info_hash'] . ".torrent";
                    if (!file_exists($file_name)) {
                        TorrentBank::where('info_hash', $value['info_hash'])->delete();
                    }
                }
            }

            $api = $download['type'] == 1 ? new QbApi($download) : new TrApi($download);
            if ($api->login_status == 0) {
                CrontabLog::create(['crontab_id' => $id, 'exception' => "下载器:" . $download->name . "连接失败,请检查配置，跳过该下载器的种子库计算"]);
                continue;
            }

            $apiTorrents = $download['type'] == 1 ? $api->getTorrentsInfo('', 'completed') : $api->getTorrentsInfo()['arguments']['torrents'];

            if (!is_array($apiTorrents)) {
                CrontabLog::create(['crontab_id' => $id, 'exception' => "下载器API返回数据不是数组"]);
                continue;
            }

            CrontabLog::create(['crontab_id' => $id, 'exception' => "通过下载器API发现" . count($apiTorrents) . "个种子"]);

            foreach ($apiTorrents as $apiTorrent) {
                // 优化和修复在此处体现
                $apiData = $this->prepareApiData($download, $apiTorrent);
                if ($this->validateTorrentFile($apiData)) {
                    try {
                        $torrent_data = file_get_contents($apiData['torrentPath']);
                        $torrent = Bencode::decode($torrent_data);
                        $info = $torrent['info'];
                        $pieces = $info['pieces'];
                        $apiData['pieces_hash'] = sha1($pieces);
                        array_push($this->currentBatch, $apiData);
                    } catch (ParseException $e) {
                        CrontabLog::create(['crontab_id' => $id, 'exception' => $e->getMessage()]);
                    }

                    // 每处理100个种子，将当前批次保存到数据库
                    if ($this->processedCount % self::BATCH_SIZE == 0) {
                        $this->saveCurrentBatchToDatabase($id);
                        $this->currentBatch = []; // 清空当前批次
                        CrontabLog::create(['crontab_id' => $id, 'exception' => "正在处理第{$this->processedCount}个种子"]);
                    }

                    $this->processedCount++;
                }
            }

            // 处理最后一个批次
            if ($this->processedCount == $totalSeeds) {
                $this->saveCurrentBatchToDatabase($id);
            }
        }

        $end_time = microtime(true);
        $execution_time = number_format((float) ($end_time - $start_time), 2);
        CrontabLog::create(['crontab_id' => $id, 'exception' => "【耗时:{$execution_time}秒】种子计算任务已完成"]);
    }

    private function prepareApiData($download, $apiTorrent)
    {
        return $download['type'] == 1 ?
            [
                'torrentPath' => $download['save_path'] . '/' . $apiTorrent['hash'] . '.torrent',
                'save_path' => $apiTorrent['save_path'],
                'info_hash' => $apiTorrent['hash'],
                'download_id' => $download['id']
            ] :
            [
                'torrentPath' => $download['save_path'] . '/' . $apiTorrent['hashString'] . '.torrent',
                'save_path' => $apiTorrent['downloadDir'],
                'info_hash' => $apiTorrent['hashString'],
                'download_id' => $download['id']
            ];
    }

    private function validateTorrentFile($apiData)
    {
        return filesize($apiData['torrentPath']) <= 5 * 1024 * 1024 && file_exists($apiData['torrentPath']);
    }

    /**
     * 数组去重函数
     */
    private function arrayUnique($array = [])
    {
        return array_values(array_unique($array, SORT_REGULAR));
    }

    /**
     * 保存当前批次到数据库
     *
     * @param int $id crontab_id
     */
    private function saveCurrentBatchToDatabase(int $id)
    {
        $batch = $this->arrayUnique($this->currentBatch);
        $bank = new TorrentBank();
        $bank->replace()->saveAll($batch);
        CrontabLog::create(['crontab_id' => $id, 'exception' => '已批量保存' . count($batch) . '条数据']);
    }
}